package com.dbsop.transport.handler;

import com.dbsop.domain.transport.info.AggResult;
import com.dbsop.common.enums.AggResultFlag;
import com.dbsop.common.util.EsFieldUtils;
import com.dbsop.common.util.ValuesUtils;
import com.dbsop.domain.info.EsFiledInfo;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.InternalMultiBucketAggregation;
import org.elasticsearch.search.aggregations.metrics.InternalNumericMetricsAggregation;
import org.elasticsearch.search.aggregations.metrics.InternalTopHits;

import java.util.*;

/**
 * 聚合处理
 */
public class TcAggHandler {

    public static List<Map<String,Object>> parseAgg(SearchResponse searchResponse){
        List<Map<String,Object>> dataMap = new ArrayList<Map<String, Object>>();
        if (searchResponse.getAggregations() == null){
            return dataMap;
        }
        List<Aggregation> aggregations = searchResponse.getAggregations().asList();
        if (aggregations == null ||aggregations.size() == 0){
            return dataMap;
        }
        return paringAggregation(aggregations,null);
    }



    public static  List<Map<String,Object>>  paringAggregation(List<Aggregation> aggregations,
                                                               Map<String,Object> resultMap){
        if (resultMap == null)resultMap = new LinkedHashMap<String, Object>();
        List<Map<String,Object>> results = new ArrayList<Map<String, Object>>();

        Map<String,Object> dataMap = new LinkedHashMap<String, Object>();
        dataMap.putAll(resultMap);

        Map<AggResultFlag, Object> aggResultsMap = parseValues(aggregations);
        //将单数据进行去
        List<AggResult> singles = (List<AggResult>) aggResultsMap.get(AggResultFlag.SINGLE);
        for (AggResult aggResult : singles) {
            dataMap.put(aggResult.getName(), aggResult.getValue());
        }
        //将无子集的数据进行合并  不必递归推下去
        // 存在子集的 继续递归
        List<List<AggResult>> subsetList = (List<List<AggResult>>) aggResultsMap.get(AggResultFlag.SUBSET);
        if (subsetList!=null && subsetList.size()>0){
            for (List<AggResult> aggResults : subsetList) {
                LinkedHashMap<String, Object> dataMapz = new LinkedHashMap<>();
                dataMapz.putAll(dataMap);
                for (AggResult aggResult : aggResults) {
                    dataMapz.put(aggResult.getName(), aggResult.getValue());
                    List<Aggregation> aggregationsZ = aggResult.getAggregations();
                    if (aggregationsZ !=null && aggregationsZ.size()>0){
                        results.addAll(paringAggregation(aggregationsZ,dataMapz));
                    }else {
                        results.add(dataMapz);
                    }
                }
            }
            // 不存在则直接将原数据返回出去
        }else {
            if (!results.contains(dataMap)){
                results.add(dataMap);
            }
        }
        for (Aggregation aggregation : aggregations) {

        }
        return results;
    }


    public static Map<AggResultFlag,Object>  parseValues(List<Aggregation> aggregations){

        Map<AggResultFlag,Object> aggResultFlagListMap = new HashMap<AggResultFlag, Object>();
        List<List<AggResult>> subset = new ArrayList<List<AggResult>>();
        List<AggResult> single = new ArrayList<AggResult>();
        for (Aggregation aggregation : aggregations) {
            parseValue(subset,single,aggregation);
        }
        aggResultFlagListMap.put(AggResultFlag.SINGLE,single);
        aggResultFlagListMap.put(AggResultFlag.SUBSET,subset);
        return aggResultFlagListMap;

    }

    /**
     * key为当前属性的值   list 为子层级
     * @param aggregation
     * @return
     */
    public static  void parseValue(List<List<AggResult>> subset, List<AggResult> single, Aggregation aggregation){
        String name = aggregation.getName();
        if (aggregation instanceof InternalMultiBucketAggregation){
            internalMultiBucketAggregationParsing(subset, (InternalMultiBucketAggregation) aggregation);
        }else if (aggregation instanceof InternalNumericMetricsAggregation){
            InternalNumericMetricsAggregation.SingleValue singleValue
                    = (InternalNumericMetricsAggregation.SingleValue) aggregation;
            single.add(new AggResult(AggResultFlag.SINGLE,name,singleValue.value()));
        }else if (aggregation instanceof InternalTopHits){
            List<List<AggResult>> lists = parseInternalTopHits((InternalTopHits) aggregation);
            if (lists.size() == 1){
                for (AggResult aggResult : lists.get(0)) {
                    single.add(aggResult);
                }
            }else if (lists.size() >1){
                subset.addAll(lists);
            }
        }
    }

    public static List<List<AggResult>> parseInternalTopHits(InternalTopHits internalTopHits){
        SearchHits hits = internalTopHits.getHits();
        List< List<AggResult>> totalResults = new ArrayList<List<AggResult>>();
        for (SearchHit hit : hits) {
            List<AggResult> aggResults = new ArrayList<AggResult>();
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            sourceAsMap.forEach((s, o) -> {
                aggResults.add(new AggResult(AggResultFlag.SINGLE,s,o));
            });
            totalResults.add(aggResults);
        }
        return totalResults;
    }

    /**
     * 接下常规分组字段
     * @param aggResults
     * @param internalTerms
     * @return
     */
    public static void  internalMultiBucketAggregationParsing(List<List<AggResult>> aggResults , InternalMultiBucketAggregation internalTerms){
        String name = internalTerms.getName();
        List<InternalMultiBucketAggregation.InternalBucket> buckets = internalTerms.getBuckets();
        for (InternalMultiBucketAggregation.InternalBucket bucket : buckets) {
            Object key = bucket.getKey();
            List<AggResult> one = new ArrayList<>(1);
            one.add(new AggResult(AggResultFlag.SUBSET,name,key,bucket.getAggregations().asList()));
            aggResults.add(one);
        }
    }


    public static <E> List<E> pareData(List<Map<String,Object>> dataMaps,Class<E> clazz){
        List<E> eList = new ArrayList<>();
        Map<String, EsFiledInfo> fieldMap = EsFieldUtils.pareFieldEsInfo(clazz);

        for (Map<String, Object> dataMap : dataMaps) {
            E t = null;
            try {
                t = clazz.newInstance();
                ValuesUtils.fieldAssignment(fieldMap,t,dataMap);
                eList.add(t);
            } catch (Exception e) {
                throw new RuntimeException(e.getCause());
            }
        }
        return eList;
    }
}
